#include "grflib_stdafx.h"
#include "../include/Skybox.h"
#include "../include/VertexFormat.h"
#include "../include/D3D10Engine.h"
#include "../include/PointerCast.h"
#include "../include/Texture.h"

using namespace grflib::engine::core;
using namespace grflib::engine::aux;
using namespace grflib::engine;


CSkyBox::CSkyBox(float distance)
    : m_distance(distance)
{
	m_bbox.Cube(0.0f);
	CreateMesh();
    CreateMaterial();
	m_nGroups = 1;
}

CSkyBox::~CSkyBox()
{
	OnDestroy();
}

BBox<float> &CSkyBox::GetBoundingBox()
{
	return m_bbox;
}

void CSkyBox::CreateMesh()
{
	static const UINT nVertex = 8;
	static const UINT nIndex = 12 * 3;

	static float faVertex[nVertex * 3] = 
	{
        -1.0f, -1.0f, -1.0f,
        1.0f, -1.0f, -1.0f,
        1.0f, 1.0f, -1.0f,
        -1.0f, 1.0f, -1.0f,
        -1.0f, -1.0f, 1.0f,
        1.0f, -1.0f, 1.0f,
        1.0f, 1.0f, 1.0f,
        -1.0f, 1.0f, 1.0f
	};

	static UINT faIndex[nIndex] = 
	{
        0, 2, 3,
        0, 1, 2, //front
        1, 6, 2,
        1, 5, 6, //right
        5, 7, 6,
        5, 4, 7, //back
        4, 3, 7, 
        4, 0, 3, //left
        3, 6, 7,
        3, 2, 6, //top
        1, 4, 5,
        1, 0, 4  //bottom
    };

	boost::shared_ptr< std::vector<float> > pVertices;
	boost::shared_ptr< std::vector<DWORD> > pIndices;

	boost::shared_ptr<std::vector<D3D10_INPUT_ELEMENT_DESC>> pElements;
	std::size_t stride;

    pVertices.reset( new std::vector<float> );
    pIndices.reset( new std::vector<DWORD> );
    pElements.reset( new std::vector<D3D10_INPUT_ELEMENT_DESC> );
    vertex_format::Mapping(*pElements, stride);

    for (UINT i = 0; i < nVertex * 3; i++)
        pVertices->push_back(faVertex[i] * m_distance);

    for (UINT i = 0; i < nIndex; i++)
        pIndices->push_back(faIndex[i]);

    m_vertexVec.push_back(pVertices);
    m_indexVec.push_back(pIndices);
    m_primitiveTopology.push_back(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    m_vertexLayouts.push_back(pElements);
    m_vertexStride.push_back(stride);

	m_materials[0] = "engine:material_sky_box";
}

void CSkyBox::CreateMaterial()
{
	boost::shared_ptr<CMaterial> pMaterial = boost::dynamic_pointer_cast<CMaterial>(_ResrcMan.FindObj("engine:material_sky_box"));
	if (!pMaterial)
	{
        pMaterial.reset( new CMaterial );
        pMaterial->diffuse[0] = 0.2f;
        pMaterial->diffuse[1] = 0.3f;
        pMaterial->diffuse[2] = 0.7f;
        pMaterial->ambient[0] = 0.2f;
        pMaterial->ambient[1] = 0.3f;
        pMaterial->ambient[2] = 0.7f;
		_ResrcMan.AddObj("engine:material_sky_box", cast::IResourceCast(pMaterial));
	}
}

void CSkyBox::SetCubeTexture(char *name)
{
	boost::shared_ptr<CMaterial> pMaterial = boost::dynamic_pointer_cast<CMaterial>(_ResrcMan.FindObj("engine:material_sky_box"));
    if (!pMaterial)
    {
        W32ERR(_T("Skybox Material not found!"));
        return;
    }
    pMaterial->textureSlots["cube"] = name;
}

HRESULT CSkyBox::PreRender(ID3D10Device *pd3dDevice,
    const D3DXMATRIX *pWorldMat,
    double fTime,
    float fElapsedTime,
    UINT pass)
{
    using namespace grflib::engine::core;
    HRESULT hr;

    bool bSucceeded = false;
    boost::shared_ptr<CEffect> pEffect;

    ////////////////////////////////////////
    //get technique
    if (pEffect = boost::dynamic_pointer_cast<CEffect>(_ResrcMan.FindObj(m_effectName)))
    {
        m_pEffect = pEffect;
        if (m_pTech = pEffect->GetTechnique(m_techniqueName))
        {
            V_RETURN( m_pTech->GetDesc(&m_techDesc) );
            bSucceeded = true;
        }
    }
    if (!bSucceeded)
    {
        W32ERR("Failed to switch to technique: %s/%s", m_effectName.c_str(), m_techniqueName.c_str());
        return E_FAIL;
    }

    ////////////////////////////////////////
    //get camera
    boost::shared_ptr<CCamera> &pCurCamera = _CamMan.GetCurrentCamera();
    if (!pCurCamera) //current camera not set
    {
        W32ERR(_T("Current camera not set!"));
        return E_FAIL;
    }

    ////////////////////////////////////////
    //set object independent shader variables
    ID3D10EffectMatrixVariable *pProjectMatVar = pEffect->GetShaderVariable("Projection")->AsMatrix();
    ID3D10EffectMatrixVariable *pViewMatVar = pEffect->GetShaderVariable("View")->AsMatrix();
    ID3D10EffectMatrixVariable *pWorldMatVar = pEffect->GetShaderVariable("World")->AsMatrix();
    if (!pProjectMatVar || !pViewMatVar || !pWorldMatVar) //uncompatible shader
    {
        W32ERR(_T("Shader uncompatible with static mesh!"));
        return E_FAIL;
    }

    D3DXMATRIX centerViewMat = *(pCurCamera->GetDXUTCamera()->GetViewMatrix());
    centerViewMat._41 = 0.0f;
    centerViewMat._42 = 0.0f;
    centerViewMat._43 = 0.0f;

    V_RETURN( pProjectMatVar->SetMatrix( (float*)pCurCamera->GetDXUTCamera()->GetProjMatrix() ) );
    V_RETURN( pViewMatVar->SetMatrix( (float*)centerViewMat ) );
    
    D3DXMatrixIdentity(&centerViewMat); //skybox always at world center
    V_RETURN( pWorldMatVar->SetMatrix( (float*)centerViewMat ) );
    return S_OK;
}